package com.chance.cc.crawler.development.scripts.taobao;

import com.alibaba.fastjson.JSON;
import com.chance.cc.crawler.core.CrawlerEnum;
import com.chance.cc.crawler.core.CrawlerRecordContext;
import com.chance.cc.crawler.core.downloader.Downloader;
import com.chance.cc.crawler.core.downloader.HttpConfig;
import com.chance.cc.crawler.core.downloader.HttpPage;
import com.chance.cc.crawler.core.downloader.HttpRequest;
import com.chance.cc.crawler.core.pipeline.result.CrawlerData;
import com.chance.cc.crawler.core.record.CrawlerRecord;
import com.chance.cc.crawler.core.record.CrawlerRequestRecord;
import com.chance.cc.crawler.core.script.crawler.CrawlerCommonScript;
import com.chance.cc.crawler.core.selector.Json;
import com.chance.cc.crawler.core.tags.KVTag;
import com.chance.cc.crawler.core.tags.crawler.CrawlerBusinessTags;
import com.chance.cc.crawler.meta.core.bean.crawler.CrawlerDomainUrls;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.Option;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;

import static com.chance.cc.crawler.development.scripts.allfeild.AICCommonField.Tag_Field_Domain_Result_Json;
import static com.chance.cc.crawler.development.scripts.allfeild.AICCommonField.Tag_Field_ECommerce_List_Content;

public class TaobaoH5ProductDetailCrawlerScript extends CrawlerCommonScript {

    private static Logger log = LoggerFactory.getLogger(TaobaoH5ProductDetailCrawlerScript.class);

    public static final String site = "h5_product";
    public static final String domain = "taobao";

    private Downloader httpDownload;
    private static Object httpDownloadObj = new Object();
    public static String startUrl = "https://h5api.m.taobao.com/h5/mtop.taobao.detail.getdetail/6.0/";
    public static final String tbProduceUrlRegular = "https://\\S*/h5/mtop.taobao.detail.getdetail/6.0/\\S*";
    public static final String tbProduceUrlFormat = "https://h5api.m.taobao.com/h5/mtop.taobao.detail.getdetail/6.0/?jsv=2.6.1&appKey=12574478&" +
            "t=%s&sign=%s&api=mtop.taobao.detail.getdetail&v=6.0&" +
            "isSec=0&ecode=0&AntiFlood=true&AntiCreep=true&ttid=2018%%40taobao_h5_9.9.9&H5Request=true&type=jsonp&" +
            "dataType=jsonp&callback=mtopjsonp1&data=%s";
    public static final String tbProduceUrlParamFormat = "{\"id\":\"%s\",\"hybrid\":\"true\",\"ttid\":" +
            "\"1608030065155@taobao_android_10.0.0\",\"itemNumId\":\"%s\",\"itemId\":\"%s\"," +
            "\"exParams\":\"{\\\"id\\\":\\\"%s\\\",\\\"hybrid\\\":\\\"true\\\"," +
            "\\\"ttid\\\":\\\"1608030065155@taobao_android_10.0.0\\\"}\",\"detail_v\":\"8.0.0\",\"utdid\":\"1\"}";

    public static String globalCookie;
    public static Object cookieObject = new Object();

    private static LinkedBlockingQueue<String> userAgents = new LinkedBlockingQueue<>();
    public static Object uaObject = new Object();

    /**
     * 脚本domain定义
     * @return
     */
    public String domain() {
        return domain;
    }

    /**
     * 进入脚本的正则列表
     */
    public void initUrlRegulars() {
        addUrlRegular(tbProduceUrlRegular);
    }

    /**
     * 是否执行脚本 ： 输入数据检查，合格的才进入脚本
     * @param crawlerRequestRecord
     * @return
     */
    public boolean crawlerCheck(CrawlerRequestRecord crawlerRequestRecord) {
        return true;
    }

    @Override
    public List<CrawlerRecord> prepareRequest(CrawlerRequestRecord requestRecord, List<CrawlerRequestRecord> supportSourceRecords) {
        List<CrawlerRecord> allItemRecords = new ArrayList<>();

        if (StringUtils.isBlank(globalCookie)){
            changeCookie(null);
        }

        if(userAgents.size() <1){
            synchronized (uaObject){
                if (userAgents.size() <1){
                    try {
                        userAgents.addAll(IOUtils.readLines(new FileInputStream("/data/chance_crawler_runner/data/ua/ua_taobao.txt"), "utf-8"));
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    }
                }
            }
        }

        HttpRequest httpRequest = requestRecord.getHttpRequest();
        try {
            if(requestRecord.tagsCreator().bizTags().hasKVTag(Tag_Field_Domain_Result_Json)){
                requestRecord.setNeedWashPage(true);
                KVTag domainResultJson = requestRecord.tagsCreator().bizTags().getCategoryTag().getKVTag(Tag_Field_Domain_Result_Json);
                CrawlerDomainUrls crawlerDomainUrls = JSON.parseObject(String.valueOf(domainResultJson.getVal()),CrawlerDomainUrls.class);
                String url = crawlerDomainUrls.getUrl();
                Json urlJson = new Json(url);
                String itemId= urlJson.jsonPath($_type + ".itemId").get();
                String time = ""+System.currentTimeMillis();
                String sign = tbHtmlSignature(httpDownload, itemId, time);
                String param = String.format(tbProduceUrlParamFormat,itemId,itemId,itemId,itemId);
                param = URLEncoder.encode(param, "utf-8");
                httpRequest.setUrl(String.format(tbProduceUrlFormat,time,sign,param));
                requestRecord.getHttpConfig().setHttpSite(site);
                requestRecord.setDownload(true);
                requestRecord.setNeedParsedPage(true);
                requestRecord.setNeedWashPage(true);
                requestRecord.setSkipPipeline(false);
                requestRecord.tagsCreator().bizTags().addCustomKV(Tag_Field_ECommerce_List_Content,url);
//            requestRecord.tagsCreator().bizTags().getCategoryTag().getKvTags().remove(Tag_Field_Domain_Result_Json); //移除
            }

            fillHttpRequestHeaders(httpRequest);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return allItemRecords;
    }

    private void fillHttpRequestHeaders(HttpRequest httpRequest) throws UnsupportedEncodingException {
        String ua = userAgents.poll();
        log.info("poll new ua: "+ ua);
        userAgents.add(ua);
        httpRequest.addHeader("Host","h5api.m.taobao.com");
        httpRequest.addHeader("Cookie",globalCookie);
        httpRequest.addHeader("User-Agent",ua);
        httpRequest.addHeader("Referer","https://h5.m.taobao.com/");

    }

    private static String tbHtmlSignature(Downloader downloader, String itemId, String time){

//        String signatureUrl = "http://192.168.1.214:8889/?itemId="+itemId+"&time="+time;
        String signatureUrl = "http://192.168.1.210:8899/encrypt/taobao?itemId="+itemId+"&time="+time;
        HttpConfig tbSignatureConfig = HttpConfig.me("test2");
        HttpRequest httpRequest = new HttpRequest();
        httpRequest.setUrl(signatureUrl);
        HttpPage download = downloader.download(httpRequest, tbSignatureConfig);
        return download.getRawText();
    }

    @Override
    public List<CrawlerRequestRecord> parseLinks(CrawlerRequestRecord crawlerRecord, HttpPage page) {
        List<CrawlerRequestRecord> crawlerRequestRecords = new ArrayList<>();

        if (!page.isDownloadSuccess()){
            log.error("TB h5 product detail proxy has error ,will retry");
            addCrawlerRecords(crawlerRequestRecords,crawlerRecord);
            crawlerRecord.setNeedWashPage(false); //下载异常，不需要清洗页面
            return crawlerRequestRecords;
        }

        try {
            Json json = new Json(page.getRawText()).removePadding("mtopjsonp1");
            Configuration conf = Configuration.defaultConfiguration().addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL,Option.SUPPRESS_EXCEPTIONS);
            json.setConfiguration(conf);
            String value = json.jsonPath($_type + ".data.apiStack[0].value").get();
            if (value == null){
                String trade = json.jsonPath($_type + ".data.trade").get();
                if (StringUtils.isNotBlank(trade) && trade.contains("/expired")){
                    String itemInfo = crawlerRecord.tagsCreator().bizTags().getCategoryTag().getKVTagStrVal(Tag_Field_Domain_Result_Json);
                    log.warn("tb h5 product {} expired!",itemInfo);
                    crawlerRecord.setNeedWashPage(false); //不需要清洗页面
                } else {
                    String itemId = json.jsonPath($_type + ".data.item.itemId").get();
                    if (StringUtils.isBlank(itemId)){
                        changeCookie(crawlerRecord.getHttpRequest().getHeaders().get("Cookie"));
                        throw new Exception("tb product detail download error");
                    }

                }
            }
        } catch (Exception e) {
            log.error("tb h5 product detail download failed,error message {}",e.getMessage());
            addCrawlerRecords(crawlerRequestRecords,crawlerRecord);
            crawlerRecord.setNeedWashPage(false); //下载异常，不需要清洗页面
        }

        return crawlerRequestRecords;
    }

    private void changeCookie(String currentCookie){
        if (StringUtils.isBlank(globalCookie)){
            synchronized (cookieObject){
                if (StringUtils.isBlank(globalCookie)){
                    globalCookie = genCookie();
                    log.warn("tb product detail init global cookie:" + globalCookie);
                }
            }
            return;
        }

        if (globalCookie.equals(currentCookie)){
            synchronized (cookieObject) {
                if (globalCookie.equals(currentCookie)){
                    globalCookie = genCookie();
                    log.warn("tb product detail change global cookie:" + globalCookie);
                }
            }
        }
    }

    private String genCookie(){
        int[] counts = new int[3];
        counts[0] = 13;
        counts[1] = 13;
        counts[2] = 13;
        String nubRandom = RandomStringUtils.random(counts[RandomUtils.nextInt(0,3)],"0123456789");
        String cnaRandom = RandomStringUtils.random(24,"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
        return "cna="+cnaRandom+";munb="+nubRandom+";unb="+nubRandom;
//        return "sgcookie=E100uRWSqrmGiUqSbF2CHw5%2FqGdIVF1TWAw1XPAMtjCTyvb%2FV%2Bgx%2FAJcfQiNtmrEKUgl6dXIEPvlLoCv%2Bv7qegjnwQ%3D%3D; enc=J8m45DeGNq9Os2lrq%2BGJTQ6o%2FH6Ln3y7XMNOdYcFW8g%2Fz0bMOrKU79aexU%2BLhQfVOPq3WpMUSiST7LJ8CoLm5pZOdA4Do1IdABeWloWhKxk%3D;" +
//                "cna="+cnaRandom;
    }

    @Override
    public void beforeDownload(CrawlerRecordContext context) {
        if (httpDownload == null){
            synchronized (httpDownloadObj) {
                if (httpDownload == null){
                    httpDownload = context.getPageDownloader();
                }
            }
        }
        super.beforeDownload(context);
    }

    @Override
    public List<CrawlerData> washPage(CrawlerRequestRecord crawlerRecord, HttpPage page) {

        List<CrawlerData> crawlerDataList = new ArrayList<>();
        try {
            Json json = new Json(page.getRawText()).removePadding("mtopjsonp1");
            String itemId = json.jsonPath($_type + ".data.item.itemId").get();

            CrawlerData crawlerArticleData = CrawlerData.builder()
                    .data(crawlerRecord,page)
                    .dataId(StringUtils.joinWith("-",domain,site,itemId))
                    .releaseTime(System.currentTimeMillis())
                    .content(page.getRawText())
                    .resultLabelTag(CrawlerEnum.CrawlerDataType.article)
                    .build();

            crawlerDataList.add(crawlerArticleData);

        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
        return crawlerDataList;
    }

    public void afterExecute(CrawlerRecordContext crawlerRecordContext) {

    }

    private void addCrawlerRecords(List<CrawlerRequestRecord> crawlerRequestRecords,CrawlerRequestRecord crawlerRecord){

        int count = 1;
        CrawlerBusinessTags crawlerBusinessTags = crawlerRecord.tagsCreator().bizTags();
        if (crawlerBusinessTags.hasKVTag("taobao_download_retry_count")){
            count = (int) crawlerBusinessTags.getCategoryTag().getKVTag("taobao_download_retry_count").getVal();
            if (count >= 200){
                log.error("tb h5 product detail download he number of retries exceeds the limit" +
                        ",request url {}",crawlerRecord.getHttpRequest().getUrl());
                return;
            }
        }

        count++;
        crawlerBusinessTags.addCustomKV("taobao_download_retry_count",count);

        CrawlerRequestRecord crawlerRequestRecord = CrawlerRequestRecord.builder()
                .turnPageRequest(crawlerRecord)
                .httpRequest(crawlerRecord.getHttpRequest())
                .needWashed(true)
                .releaseTime(System.currentTimeMillis())
                .copyBizTags()
                .notFilterRecord()
                .build();
        crawlerRequestRecords.add(crawlerRequestRecord);
    }
}
